Hey there.
Welcome back.
In this lecture we're going to be exploring the task library which gives us very useful sets of functions
for scheduling things to happen in our code.
It's called the task library because it works directly with the Roblox Task Scheduler, which basically
sets what order code should be executed in the game engine.
Now, you've already seen us use the task dot wait function, and this function goes and yields the
current thread of execution for a set amount of time that we pass here.
And the task wait function also returns a number, and this number represents the amount of time that
our task wait function actually yielded for.
So we'll call this yielded time, and we'll wait for one second.
And then we can print.
This yielded time into the console.
And let's go ahead and see actually how long our task dot wait for one second actually yielded for.
So if we run the game here and look inside of our console, you can see that our task wait function
actually yielded for about 1.01165 seconds.
The reason why this value isn't perfect is because of how the task scheduler works.
Yielded threads are going to be checked on a frame by frame basis, which is causing this small discrepancy
in the timestamp of task dot.
Wait, so it's never going to be perfectly waiting for one second, but it's going to be pretty stinkin
close.
Now let's go ahead and take a look at some of the other functions the task library has to offer.
One of them is called task dot spawn.
So let me go ahead and refer to my task library and get the spawn function.
And it says here it says calls or resumes a function slash coroutine immediately through the engine
scheduler.
So as you can see, it's working directly with the task scheduler to make this happen.
So we can pass a function here.
And what's going to happen is it's going to execute or spawn this function immediately in a coroutine
thread.
And if you remember back in our lecture about coroutine threads, the coroutine thread data type isn't
actually what we would think as a thread on a processor in a computer.
And that's because Lua is a single threaded programming language, so everything is still going to be
executed on one computer thread.
But we do have this idea of coroutine threads that allow us to handle multiple different tasks at the
same time.
So inside of this task dot spawn function, I'm just going to have a for loop that's going to loop ten
times.
So we'll do I to 1 to 10.
And we'll just print I out into the console.
And then outside of this function, I'm going to print something like a statement and say this was not
blocked.
Now because Lua is single threaded, what's going to happen is the interpreter is going to go through
here.
It's going to hit this function call for task dot spawn.
And it's going to be like, oh, okay, I got to go in here and execute this code immediately.
So what task dot spawn does is it spawns a new coroutine thread and executes it right away immediately.
So it's going to go through here and execute this print statement ten times.
So it's going to print that ten times in the console.
And then it's going to print.
This was not blocked.
So let me go ahead and comment this out and let's run our game.
And inside of the console.
As you can see, one, two, three all the way to ten.
And then afterward our print statement of this was not blocked, got executed.
Now if our tasked spawn function had a yield in here, for example, through every iteration of our
for loop, let's say we yielded for a short period of time.
Well, now what's going to happen is the main thread of execution is going to go through here.
It's going to hit this for loop and print the first index which will be one.
And it's going to hit this yield statement.
And it's going to be like okay, now it's time for me to exit out of here and execute any other tasks
that are currently waiting to be executed.
And one example of that would be our print statement down here.
So it's going to hop out of our for loop and come down and print this statement.
And then once it has to come back in here, once this yield statement is up, then it'll print the second
letter and then the third and so on and so forth.
And every time it hits this function to yield the current thread, the current thread is going to hop
out and execute any other important game stuff that's happening in our game.
So if we run our game now, what you're going to see is instead of our print statement being printed
at the end, it's going to be printed second, because first it printed one, then it hit that yield
statement which caused it to go down and print.
This was not blocked.
And then it came back to our for loop and printed the rest of our numbers.
Now there's another function in the task library called task dot defer.
And this function is extremely similar to task dot spawn.
But there is a slight difference.
And actually Roblox recommends for you to use task dot defer over task dot spawn if needed.
And what task dot defer does.
We're going to call defer here.
And actually let's go ahead and read what it says.
It says calls slash resumes a function or coroutine on the next resumption cycle.
What does that mean.
Well you can basically think of the resumption cycle as all of the stuff that needs to be executed in
the current frame.
So the first thing that needs to be executed is like rendering all the stuff on the screen.
The next thing that needs to be executed is handling all the physics, and then the next one would be
other stuff, until eventually you'll get to a section in the frame at the end of a frame called deferred
threads.
And this section of deferred threads is going to be a queue of deferred tasks that need to be executed
by the game engine and task.
Dot defer allows us to add a coroutine thread into that queue of deferred tasks, and execute it at
a later point in the current frame.
So that's why it's called task dot defer.
It's because we're deferring a task to be executed at a later point in the current thread.
So let's go ahead and defer a new function here.
And we're going to be doing the exact same thing that we did up here.
We're just going to paste that there.
But I'm not going to yield here.
And then let me go ahead and comment this out.
And I'm going to copy this and print this down here.
Now what's going to happen is because this coroutine thread is going to be pushed at a later point in
the current frame, this is going to execute first and then this is going to execute afterwards.
So let's go ahead and run our game.
And if you look as you can see, our print statement executed first, this was not blocked.
And then afterwards our loop went through and executed all of the different numbers.
And that's because that for loop that was inside of that thread got pushed into that queue of deferred
tasks.
So task De-spawn and task dot defer are going to both execute in the same frame.
The only difference is at what point in the frame they are going to be executed.
Task dot spawn is going to execute this thread right away immediately, while task dot defer is going
to place it into that queue of deferred tasks at a later point in the current frame.
That's the difference between those two functions.
Now Roblox themselves recommend for you to use task Dot defer most of the time, unless you really need
to spawn a task or a thread right away and have it execute immediately.
For the most part, you should be using task dot defer.
Now, the next function we're going to take a look at is called task dot delay.
And this one's a pretty neat one because it allows us to pass a number and then a function or a thread
and it says schedules a function slash coroutine to be called slash.
Resumed on the next heartbeat after the given duration in seconds has passed without throttling.
So basically we can pass a number here to define how long we want to wait before we execute this function.
So for example, I can pass a value of five here and then a function.
And this function will execute five seconds in the future.
So let me go ahead and comment this out up here.
And let's go ahead and copy this paste that down there.
And then we can just do the same thing with the for loop.
So I'll just paste that there and let me get rid of these comments.
So now we're going to have a coroutine thread that is currently delayed for five seconds.
So five seconds in the future is going to execute this for loop.
So that means this print statement down here is going to execute first.
So let's go ahead and run the game.
There is our print statement and now we have to wait five seconds.
And then there we go.
Our for loop executed and printed our ten numbers.
And if you take a look at the timestamp, it has waited roughly five seconds to then execute that thread
in the future.
Now you keep hearing me calling these coroutine threads, and that's because the task dot delay, the
task dot defer, and the task dot spawn function all return a coroutine thread data type.
So if we actually take a look at task dot delay and we look at the end, it says it returns a thread.
The thread data type which is a coroutine thread.
And that means we're able to store this thread in a variable.
And because we now have this thread, we're able to check many different things about this thread,
such as the status of the thread.
And we're also able to cancel these threads with another function that we're going to take a look at
in a little bit.
But for example, what I'm going to do down here is I'm going to print and I'm going to use the coroutine
library and use the status function to print the current status of this thread right here that is delayed.
So we're going to call coroutine dot status and then pass our thread.
And let's go ahead and see what it prints inside of the console.
So as you can see, it prints out suspended.
And that's because that coroutine thread is currently suspended and we're waiting for it to execute
at some point in the future.
And then after it goes through and executes through that for loop and it reaches the end of the function,
then that thread is going to be placed in a dead state.
Now, because each of these functions return a thread data type, we are able to cancel them using a
function in the task library called task dot cancel.
So if we take a look at the task cancel function it says cancels a thread preventing it from being resumed.
And all we need to do is pass a thread here.
So what I'm going to do is I'm also going to comment out this up here and let's go ahead and spawn a
function.
And we'll just do the same kind of for loops.
So for I equal to 1 to 10 we'll print out I.
And then I'll put a yield statement in here.
And we'll yield for like one second.
And then we can go ahead and store that thread in a variable.
And now let's say I wanted to cancel this for loop after let's say three seconds of executing.
So let's go ahead and yield for three seconds down here.
And then afterwards we're going to cancel the current thread.
And then I'm going to print a message called thread cancel.
We'll say something like that.
So now what should happen is this for loop should print.
I believe it should print one, two, three, but it might print 1 to 2.
And then afterwards we're going to cancel that thread.
We'll print out thread cancelled.
And now any of the other code that we were supposed to execute in here, like the additional iterations
through the loop, we're no longer going to execute it because we've canceled the thread.
So if we take a look, we got one, two, three and then it said thread canceled and now we're done.
So just like that, if we wanted to prematurely cancel or stop a particular thread from executing,
we can do so using the task dot cancel function.
And we can also do the same thing with an infinitely executing thread.
So like if we had a while loop in here.
And this while loop was simply just printing out hello or something like that.
And then we'll put a yield statement here.
What we'll go ahead and do is we'll actually just wait for one second, and then after one second we'll
cancel that thread and print out thread cancel.
So if we run.
There you go.
We got 25 hello statements printed into our console, and then the thread got cancelled.
And the while loop is no longer executing, which is pretty cool.
Now, I would not recommend canceling infinite loops using task dot cancel.
You should instead set it up in a way where your while loop takes a condition instead, and when that
condition is set to false, then the loop will end.
This just makes it a bit clearer to anyone who is reading your code exactly what is happening, instead
of your infinite loop being cancelled somewhere else in your script.
Because if I look at a while true do loop, I'm assuming.
Hey, this is going to be executing forever throughout the entire duration of our game, but if we're
cancelling that while loop somewhere else in our script, then my assumption was not correct.
So instead, you should set these while loops up in a way where you'll have a condition instead, and
then set that condition to false when you want your while loop to stop executing.
There are two other functions in the task library.
These functions are.
Let me see if I can find them.
They are tasks dot synchronize and task dot d synchronize.
Do not worry about these functions because they relate to parallel Lua, which is Lua code executing
on multiple processor threads.
And that's too complex to be discussing about this early in the course.
So again, do not worry about these two other functions.
Okay, so let's go through a quick recap of everything we've just learned.
We know the task wave function allows the current thread to yield, and it returns to us a number that
defines the actual amount of time that the function yielded for the task.
Spawn function allows us to spawn a new coroutine thread and execute it right away at the current frame,
and the task dot defer function allows us to spawn a coroutine thread in the current frame, but instead
we defer it and place it in a queue so it executes at a later point in the current frame.
We also know about the task dot delay function, which allows us to delay a thread or a function from
executing at some point in the future.
If we were to set this value here to zero, then this function or thread is going to be executing at
the next frame.
Last but not least, we know about the task dot cancel function, which allows us to pass a thread which
is returned from our task dot spawn or task dot delay or task dot defer functions, and we're able to
cancel that thread and prevent it from executing.
So other than that, the task library is an incredibly useful and important library that you'll be using
a lot in your code.
There are so many things you can do with the task library, because it helps us schedule our code,
and we'll be using this library a lot in our projects.
Thanks for watching and I'll see you in the next lecture.